/************************** Notice ***************************
 This is free software.
 You may redistribute, modify and make copies of this software
 provided that this notice is kept unaltered.
 ************************************************************/

/************************* Code monkeys **********************
 Alexander "Koskenkorva" Rajula - chris@nerdmail.nu
 ************************************************************/

#include <math.h>

// ************************* Operation constants *************
// Injector flows at 3 bar (43.5 psi) measured in cubic centi-
// metres (millilitres).
const byte inj_flow[] = {340,340,340,340,340,340};
const byte num_cylinders = 6;

// Throttle constants for determining mode of operation.
// Volvo uses 0.5v for idle and 4.6v for WOT. 
// Arduino quantizes the analog 0-5v input to 1024 values.
const float voltage_multiplier = 0.0048828125; // 5/1024
const float throttle_low = 0.5;
const float throttle_wot = 4.6;
const float throttle_idle = throttle_low*voltage_multiplier; // (5/1024)*0.5
const float throttle_full = throttle_wot*voltage_multiplier; // (5/1024)*4.6

// Fuel constants.
// The stochiometric ratio for gasoline is 14.7:1 air:fuel mass ratio.
// The stochiometric ratio for E85 (summer blend) is 9.8526:1 air:fuel mass ratio.
// The stochiometric ratio for E85 (winter blend) is 10.6975 air:fuel mass ratio.
// The stochiometric ratio for E100 is 9.0078:1 air:fuel mass ratio.
const float fraction_e85_summer = 0.1014960518;
const float fraction_e85_winter = 0.093479785;
const float fraction_e199 = 0.1110148982;
const float fraction_gas = 0.06802721088;

// *********************** Output maps ***********************
// Map spark plugs to output pins.
const byte spark[] = {1,2,3,4,5,6};

// Map injectors to output pins.
const byte injector[] = {7,8,9,10,11,12};

// Map fuel pump to output pin.
const byte fuel_pump = 13;

// Map engine fan to output pins.
const byte fan_low = 14;
const byte fan_high = 15;

// Map idle control valve to output pin.
const byte idle_control_valve = 16;

// *********************** Input maps ***********************
// Map throttle to input pin.
const byte throttle = A0;

// Map tachometer (REV counter) to input pin.
const byte tachometer = A1;

// Map crank position sensor to input pin.
const byte crank = A2;

// Map air temperature to input pin.
const byte air_temp = A3;

// Map knock sensor inputs to input pins.
const byte knock_rear = A4;
const byte knock_front = A5;

// Map coolant sensor to input pin.
const byte coolant_temp = A6;

// Map oxygen sensor for lambda control to input pin.
const byte lambda = A7;

// Map air intake mass to input pin.
const byte air_mass = A8;

// *********************** Routines ***********************
void setup(){
  // Set output pins.
  for(int i = 0; i < num_cylinders; i++){
   pinMode(spark[i],OUTPUT);
   pinMode(injector[i],OUTPUT); 
  }
  pinMode(fuel_pump,OUTPUT);
  pinMode(fan_low,OUTPUT);
  pinMode(fan_high,OUTPUT);
  pinMode(idle_control_valve,OUTPUT);
  
  // Set input pins.
  pinMode(throttle,INPUT);
  pinMode(tachometer,INPUT);
  pinMode(crank,INPUT);
  pinMode(air_temp,INPUT);
  pinMode(knock_rear,INPUT);
  pinMode(knock_front,INPUT);
  pinMode(coolant_temp,INPUT);
  pinMode(lambda,INPUT);
  pinMode(air_mass,INPUT);
  
  // Initialize serial output.
  Serial.begin(115200);
  Serial.println("            _,'|             _.-''``-...___..--';)");
  Serial.println("           /_ \'.      __..-' ,      ,--...--'''");
  Serial.println("          <\'   .`--'''       `     /'");
  Serial.println("           `-';'               ;   ; ;");
  Serial.println("     __...--''     ___...--_..'  .;.'");
  Serial.println("    (,__....----'''       (,..--''");
  Serial.println("************************************************************************************************************");
  Serial.println("It's time to kick ass and chew bubble gum... and I'm all outta gum.");
  Serial.println("************************************************************************************************************");
  Serial.println("------------------------------------------------------------------------------------------------------------");
  Serial.print("Throttle [V]\t");
  Serial.print("Rev [RPM]\t");
  Serial.print("Intake air temp [C]\t");
  Serial.print("Air mass [kg/h]\t");
  Serial.print("Coolant temp [C]\t");
  Serial.print("Lambda [V]\t\n");
  Serial.println("------------------------------------------------------------------------------------------------------------");
}

void loop(){
  print_data();
}

void print_data(){
  Serial.print(vfa(throttle));
  Serial.print("\t\t");
  Serial.print(tachometer_signal_to_rpm());
  Serial.print("\t\t");
  Serial.print(air_temp_voltage_to_centigrade(vfa(air_temp)));
  Serial.print("\t\t\t");
  Serial.print(air_mass_voltage_to_mass(vfa(air_mass)));
  Serial.print("\t\t");
  Serial.print(coolant_voltage_to_centigrade(vfa(coolant_temp)));
  Serial.print("\t\t\t");
  Serial.print(analogRead(lambda)*voltage_multiplier);
  Serial.println("");
  Serial.flush();
}

// Calculate voltage input from analog input signal.
float vfa(const float analog){
 return analogRead(analog)*voltage_multiplier; 
}

float coolant_voltage_to_centigrade(const float voltage){
  // This formula is based on a regression analysis from OpenOffice Calc, using the data set at:
  // http://www.volvoclub.org.uk/faq/EngineSensors.html#DiagnosingECTFailures
 return -43.4*log(voltage)+48.67; 
}

float air_temp_voltage_to_centigrade(const float voltage){
  // Based on a 10kOhm voltage division supplied with 5v.
  float resistance = (10000*voltage)/(5-voltage);
  // Derived from Bosch HFM5 sensors datasheet.
  return -28.5*log(resistance)+248.88;
}

float air_resistance_to_centigrade(const float resistance){
  // Derived from Bosch HFM5 sensors datasheet.
  return -28.5*log(resistance)+248.88;
}

float air_mass_voltage_to_mass(const float voltage){
 // This is definitely not correct.
 return 3.9*pow(voltage,3.18); 
}

int tachometer_signal_to_rpm(){
 // This should probably do something fancy to figure out engine RPM.
 return analogRead(tachometer); 
}
